/**
* @file key.c
* @brief 按键驱动用于学习Linux中断
* @author bingfengxiao(bingfengxiao@163.com)
* @version 1.0
* @date 2012-09-25
*/

#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/cdev.h>
#include <linux/fs.h>
#include <asm/errno.h>
#include <linux/sched.h>
#include <asm/irq.h>
#include <linux/irq.h>
#include <linux/interrupt.h>
#include <linux/gpio.h>

#define KEY_IRQ IRQ_EINT0
#define KEY_INT_PIN S3C2410_GPF(0)

int smdk2410_key_open(struct inode *pnode, struct file *pfile);
ssize_t smdk2410_key_read(struct file *pfile, char __user *buffer, size_t buffer_size,
	 loff_t *offset);
int smdk2410_key_release(struct inode *pnode, struct file *pfile);

dev_t key_dev_id;
struct cdev key_cdev;
struct file_operations key_fops = {
	.owner = THIS_MODULE,
	.open = smdk2410_key_open,
	.read = smdk2410_key_read,
	.release = smdk2410_key_release,
};

irqreturn_t smdk2410_interrupt_isr(int irq, void *dev_id){
	printk("Enter %s\n", __FUNCTION__);
	return IRQ_HANDLED;
}



/**
* @brief smdk2410_key_open 
*
* @param pnode
* @param pfile
*
* @return 
*/
int smdk2410_key_open(struct inode *pnode, struct file *pfile){
#if 0
	int iret;
	
	printk("Go to request irq\n");
	/** 与硬件平台相关，s3c2410不支持*/ 
#if 0
	iret = request_gpio(KEY_IRQ, "smdk2410-key");
	if (iret < 0){
		printk("ERROR: request gpio failed!\n");
		return -ENOBUFS;
	}
#endif
	
	s3c2410_gpio_cfgpin(KEY_INT_PIN, S3C_GPIO_SFN(2));
	//gpio_to_irq(KEY_INT_PIN);
	
	iret = request_irq(KEY_IRQ, smdk2410_interrupt_isr, 
		IRQF_TRIGGER_FALLING, "smdk2410-key", NULL);
	/* irq_set_irq_type(KEY_IRQ, IRQF_TRIGGER_FALLING)*/
	if (iret < 0){
		printk("request irq failed!\n");
		return -ENOBUFS;
	}
	printk("Request irq OK!\n");
#endif
	
	return 0;
}

/**
* @brief smdk2410_key_release 
*
* @param pnode
* @param pfile
*
* @return 
*/
int smdk2410_key_release(struct inode *pnode, struct file *pfile){
#if 0
	printk("Go to free irq\n");
	free_irq(KEY_IRQ, NULL);
	printk("Free irq OK!\n");
#endif
	return 0;
}

/**
* @brief smdk2410_key_read 
*
* @param pfile
* @param buffer
* @param buffer_size
* @param offset
*
* @return 
*/
ssize_t smdk2410_key_read(struct file *pfile, char __user *buffer, size_t buffer_size, loff_t *offset){
	return 0;
}

/**
* @brief smdk2410_key_init 
*
* @return 
*/
static int __init smdk2410_key_init(void){
	int iret;
	iret = alloc_chrdev_region(&key_dev_id, 0, 1, "smdk2410-key");
	if (iret < 0){
		printk("Alloc key device ID failed!\n");
		return -ENOBUFS;
	}
	cdev_init(&key_cdev, &key_fops);
	key_cdev.owner = THIS_MODULE;
	iret = cdev_add(&key_cdev, key_dev_id, 1);
	if (iret < 0){
		unregister_chrdev_region(key_dev_id, 1);
		printk("Add key device failed!\n");
		return -ENOBUFS;
	}


	/** 这段代码本来应该放在open里面的，放到这儿仅是为了测试方便*/
	printk("Go to request irq\n");
	/** 与硬件平台相关，s3c2410不支持*/ 
#if 0
	iret = request_gpio(KEY_IRQ, "smdk2410-key");
	if (iret < 0){
		printk("ERROR: request gpio failed!\n");
		return -ENOBUFS;
	}
#endif
	
	/** 这两个函数都可以用*/
	//s3c2410_gpio_cfgpin(KEY_INT_PIN, S3C_GPIO_SFN(2));
	/** 用来测试是否只用request_irq()函数就可以成功申请中断*/
	//s3c2410_gpio_cfgpin(KEY_INT_PIN, S3C_GPIO_OUTPUT);
	//gpio_to_irq(KEY_INT_PIN);
	
	iret = request_irq(KEY_IRQ, smdk2410_interrupt_isr, 
		IRQF_TRIGGER_FALLING, "smdk2410-key", NULL);
	//irq_set_irq_type(KEY_IRQ, IRQF_TRIGGER_FALLING);
	if (iret < 0){
		printk("request irq failed!\n");
		return -ENOBUFS;
	}
	printk("Request irq OK!\n");

	/** 用来测试是否只用request_irq()函数就可以成功申请中断*/
	//s3c2410_gpio_cfgpin(KEY_INT_PIN, S3C_GPIO_OUTPUT);

	printk("Module init OK!\n");
	return 0;

}

/**
* @brief smdk2410_key_exit 
*
* @return 
*/
static void __exit smdk2410_key_exit(void){

	printk("Go to free irq\n");
	free_irq(KEY_IRQ, NULL);
	printk("Free irq OK!\n");


	cdev_del(&key_cdev);
	unregister_chrdev_region(key_dev_id, 1);
	printk("Module exit!\n");
	return;
}

module_init(smdk2410_key_init);
module_exit(smdk2410_key_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Bingfeng Xiao");
MODULE_DESCRIPTION("SMDK2410 interrupt test");
